package ss13aufgabe1b;

public class Porsche911GT2RS
{
    // Gewicht in KG
    private final double Mass = 1445.0; //[kg]
    // Max. Leistung in KW
    private final double PowerPropMax = 456000.0;
    // Max. Speed in m/s
    private final double SpeedMax = 91.6;
    // Min. Speed in m/s
    private final double SpeedMin = 0.0;
    // Erdbeschleunigung in m/(s*s)
    private final double AccEarth = 9.81;
    // max. Kraft
    private final double ForcePropMax = Mass * AccEarth;
    // max. DragConst
    private final double DragConst = Math.abs(PowerPropMax / Math.pow(SpeedMax,3));

    // Range für Level
    private final double MinLevel = 0.0;
    private final double MaxLevel = 1.0;

    // Umrechnug von m/s nach km/h
    private final double meterPerSecToKilometersPerHoursFactor = 3.6;

    // Antriebshebel
    private double Level;
    // Zeit in S
    private double Time;
    // Position
    private double Pos;
    // Geschwindigtkeit
    private double Speed;


    // Der Konstruktor
    public Porsche911GT2RS() throws RangeException
    {
        // Variablen auf Default-Werte setzen
        reset();
    }

    // Range prüfen
    // Error => RangeException
    private boolean checkLevelRange(double aLevel) throws RangeException
    {
        // Rückgabe
        boolean bReturn = true;

        // Eingabe im gültigen Bereich?
        if( !((aLevel <= MaxLevel) && (aLevel >= MinLevel )) )
        {
            // Nicht aktzeptiert
            bReturn = false;
            throw new RangeException();
        }

        // und ab damit
        return bReturn;
    }

    // Setzt alle wichtigen Variablen
    public void set(double Time, double Pos, double Speed, double Level) throws RangeException
    {
        if( checkLevelRange(Level) )
        {
            this.Time  = Time;
            this.Pos   = Pos;
            this.Speed = Speed;
            this.Level = Level;
        }
    }

    // Setzt alle wichtigen Variablen auf Default-Werte zurück
    public void reset() throws RangeException
    {
        set(0.0,0.0,0.0,0.0);
    }

    // Einen Simulations-Schritt ausführen
    public void step(double DeltaTime, double Level) throws RangeException
    {
        // Manipulation nur durchführen, wenn Level gültig ist
        if( checkLevelRange(Level) )
        {         
            // Wie viel % der Leistung
            double powerProp = PowerPropMax * Math.abs(Level);

             // Formeln anwenden
            double forcePropAbs = Math.min(ForcePropMax, powerProp/(Math.max(0.0001, Math.abs(Speed))));
            double forceProp = forcePropAbs * Math.signum(Level);
            double forceDrag = DragConst * (Speed*Speed) * Math.signum(-Speed);
            double force = forceProp + forceDrag;
            double acc = force / Mass;

            // Interne Werte setzen
            Time += DeltaTime;
            Pos = Pos + (Speed * DeltaTime);
            Speed = Speed + (acc * DeltaTime);
        }
    }

    // Aktuelle Geschwindigkeit in km/h
    public double getSpeed()
    {
        return Speed * meterPerSecToKilometersPerHoursFactor;
    }

    // Aktuelle Position in Metern
    public double getPos()
    {
        return Pos;
    }

    // Aktuelle Simulationszeit in Sekunden
    public double getTime()
    {
        return Time;
    }
}

